package com.shareroad.dubboweb.frame.handler.impl;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.config.ReferenceConfig;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.shareroad.dubboweb.frame.base.Constants;
import com.shareroad.dubboweb.frame.dao.data.DubboWebData;
import com.shareroad.dubboweb.frame.dao.data.dubboServiceCache;
import com.shareroad.dubboweb.frame.dao.po.DubboParamPO;
import com.shareroad.dubboweb.frame.handler.ApiHandler;
import com.shareroad.dubboweb.frame.model.entity.ErrorCode;
import com.shareroad.dubboweb.frame.model.entity.ReturnObject;
import com.shareroad.dubboweb.frame.util.JarUtil;

@SuppressWarnings({"rawtypes","unchecked"})
public class ApiHandlerImpl implements ApiHandler {
	
	private static final Logger logger = LoggerFactory.getLogger(ApiHandlerImpl.class);

	private ReferenceConfig referenceConfig; 

	public ReferenceConfig getReferenceConfig() {
		return referenceConfig;
	}

	public void setReferenceConfig(ReferenceConfig referenceConfig) {
		this.referenceConfig = referenceConfig;
	}

	@Override
	public ReturnObject invokeDubboService(String interfaceName, String version,String method,
			JSONObject params) {
		ReturnObject ro = new ReturnObject();
		// 取得参数名称对应参数类型的map
		List<DubboParamPO> methodParamTypes = DubboWebData.getParamTypes(interfaceName, method);
		if (null == methodParamTypes) {
			ro.setStatus(Constants.HTTP_500);
			ro.setErrorCode(ErrorCode.api_data_not_service.getCode());
			ro.setErrorDesc(ErrorCode.api_data_not_service.getDesc());
			return ro;
		}
		Class cls = null;
		try {
			//String jarName = DubboWebData.getDubboWeb(interfaceName, method).jarName;
			//ClassLoader clazzLoader = ClassLoaderCache.getClassLoader(jarName);
			//cls = Class.forName(interfaceName,true,clazzLoader);
			cls = Class.forName(interfaceName,false,JarUtil.getDubboWebClassLoader());
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			ro.setStatus(Constants.HTTP_500);
			ro.setErrorCode(ErrorCode.api_class_not_find.getCode());
			ro.setErrorDesc(ErrorCode.api_class_not_find.getDesc());
			return ro;
		}
		Object dubboService = dubboServiceCache.getService(interfaceName) ;
		if(null == dubboService ){
			try{
				//referenceConfig.setInterface(interfaceName);
				referenceConfig.setInterface(cls);
				if(!StringUtils.isBlank(version)){
					referenceConfig.setVersion(version);
				}
				// 和本地bean一样使用xxxService
				//referenceConfig.
				dubboService = referenceConfig.get(); // 注意：此代理对象内部封装了所有通讯细节，对象较重，请缓存复用
				if(null == dubboService){
					throw new Exception();
				}
				dubboServiceCache.addService(interfaceName, dubboService);
			}catch(Exception e){
				//dubboServiceCache.removeService(interfaceName);
				e.printStackTrace();
				ro.setStatus(Constants.HTTP_500);
				ro.setErrorCode(ErrorCode.api_link_dubbo_error.getCode());
				ro.setErrorDesc(ErrorCode.api_link_dubbo_error.getDesc());
				return ro;
			}
		}
		//反射执行
		
		Class[] paramTypes = new Class[methodParamTypes.size()];
		Object[] paramValues = new Object[methodParamTypes.size()];
		int i=0;
		for(DubboParamPO dubboParam : methodParamTypes){
			try {
				Class typeCls = convertType(dubboParam.type);
				paramTypes[i] = typeCls;
				String pname = (null == dubboParam.name || "".equals(dubboParam.name)) ? dubboParam.sourceName : dubboParam.name;
				paramValues[i] = JSON.parseObject(JSON.toJSONString(params.get(pname)), typeCls);
				i ++ ;
			} catch (Exception e) {
				e.printStackTrace();
				ro.setStatus(Constants.HTTP_500);
				ro.setErrorCode(ErrorCode.api_param_type_not_load.getCode());
				ro.setErrorDesc(ErrorCode.api_param_type_not_load.getDesc()+" : "+e.getMessage());
				return ro;
			}
		}
		Method setMethod =null;
		try {
			setMethod = cls.getDeclaredMethod(method,paramTypes);
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
			ro.setStatus(Constants.HTTP_500);
			ro.setErrorCode(ErrorCode.api_dubbo_not_method.getCode());
			ro.setErrorDesc(ErrorCode.api_dubbo_not_method.getDesc());
			return ro;
		} catch (SecurityException e) {
			e.printStackTrace();
			ro.setStatus(Constants.HTTP_500);
			ro.setErrorCode(ErrorCode.api_dubbo_method_security_execption.getCode());
			ro.setErrorDesc(ErrorCode.api_dubbo_method_security_execption.getDesc());
			return ro;
		}
		Object result = null;
		try {
			result = setMethod.invoke(dubboService, paramValues);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			ro.setStatus(Constants.HTTP_500);
			ro.setErrorCode(ErrorCode.api_dubbo_illegal_access_exception.getCode());
			ro.setErrorDesc(ErrorCode.api_dubbo_illegal_access_exception.getDesc());
			return ro;
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
			ro.setStatus(Constants.HTTP_500);
			ro.setErrorCode(ErrorCode.api_dubbo_illegal_argument_exception.getCode());
			ro.setErrorDesc(ErrorCode.api_dubbo_illegal_argument_exception.getDesc());
			return ro;
			
		} catch (InvocationTargetException e) {
			e.printStackTrace();
			ro.setStatus(Constants.HTTP_500);
			ro.setErrorCode(ErrorCode.api_dubbo_invocation_target_exception.getCode());
			ro.setErrorDesc(ErrorCode.api_dubbo_invocation_target_exception.getDesc());
			return ro;
		}
		if(!"void".equals(setMethod.getReturnType().getSimpleName())){
			/*try{
				String returnType = setMethod.getReturnType().getName();
				if(!isBaseType(returnType)){
					result = (Class.forName(returnType)).cast(result);
				}
			}catch(Exception e){}*/
			ro.setReturnContent(result);
		}else{
			ro.setReturnContent("void");
		}
		ro.setStatus(Constants.HTTP_200);
		return ro;
	}
	
	private Class convertType(String type) throws ClassNotFoundException {
		if(type.contains(".")){
			return Class.forName(type);
		}
		if(type.equals("boolean")){
			return boolean.class;
		}else if(type.equals("char")){
			return char.class;
		} else if(type.equals("byte")){
			return byte.class;
		}else if(type.equals("short")){
			return short.class;
		}else if(type.equals("int")){
			return int.class;
		}else if(type.equals("long")){
			return long.class;
		}else if(type.equals("float")){
			return float.class;
		}else if(type.equals("double")){
			return double.class;
		}
		return null;
	}
	
	private boolean isBaseType(String type){
		
		if(type.equals("boolean")){
			return true;
		}else if(type.equals("char")){
			return true;
		} else if(type.equals("byte")){
			return true;
		}else if(type.equals("short")){
			return true;
		}else if(type.equals("int")){
			return true;
		}else if(type.equals("long")){
			return true;
		}else if(type.equals("float")){
			return true;
		}else if(type.equals("double")){
			return true;
		}
		return false;
	}
	
	

}
